package gui;
import javax.swing.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.text.*;
import javax.swing.event.*;
import java.awt.geom.*;
import java.util.concurrent.*;
import java.io.*;
import java.util.*;
import gameManager.*;
import zarzadcyZdarzen.*;
import java.nio.charset.*;
 
/**
 * Okno główne programu, z chatem, listą stołów, etc.
 * Tutaj też następuje - po prawidłowym połączeniu się - zbudowanie obiektów TableManagera, EventDispachera i SendingAdaptera 
 * @author Konrad Sobczak
 * @author Kamil Nowak
 */
public class MainWindow extends JFrame {
	private JTextPane chat;
	private JTextField wpisywanie;
	private static SimpleAttributeSet bold = new SimpleAttributeSet();
	private static SimpleAttributeSet normal = new SimpleAttributeSet();
	private TableManager menadzerStolow;
	private WyborUrlLogin wyborUrlLogin;
	private String imie;
	private EventDispacher ed;
	private JMenuItem pokaz;
	private Socket socket=null;
	private NazwaStoluWybor wyborNazwaStolu;
	private final MainWindow ja_sama;
	private SendingAdapter sad=null;
	private long id_user;
	private JList listaStolow;
	private DefaultListModel modelListyStolow = new DefaultListModel();
	private ConcurrentHashMap<String,Integer> mapaIdStolow;
	
	/**
	 * konstruktor
	 *
	 */
	public MainWindow(){
		
		mapaIdStolow = new ConcurrentHashMap<String,Integer>();
		StyleConstants.setBold(bold,true);
		ja_sama = this;
		this.setTitle("Klient szachowy... - niezalogowany");
			
		NowaSzachownica ns = new NowaSzachownica(); 
		
		JMenu menuPlik = new JMenu("Klient");
		menuPlik.setMnemonic('K');
		pokaz = menuPlik.add(ns);
		pokaz.setText("Nowy stół");

		JMenuItem zmienSerwer = menuPlik.add(new AbstractAction("Podłącz się"){
			public void actionPerformed(ActionEvent zdarzenie)
				{
				try{
					   if(socket!=null && !socket.isClosed()){
						   PrintWriter out=null;
				    	   out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),Charset.forName("ISO-8859-2")),true);
						   out.println("EXIT");
				    	   socket.close();
					   }
					 }
					  catch(IOException e){
						  JOptionPane.showMessageDialog(null,e.getMessage(),"Błąd połączenia",JOptionPane.ERROR_MESSAGE); 
					 }
				ja_sama.oknoUrlLogin();
				}
		});
		
		JMenuItem koniec = menuPlik.add(new AbstractAction("Zakończ"){
			public void actionPerformed(ActionEvent zdarzenie)
				{
				try{
					   if(socket!=null && !socket.isClosed()){
						   PrintWriter out=null;
				    	   out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),Charset.forName("ISO-8859-2")),true);
						   out.println("EXIT");
				    	   socket.close();
					   }
				      ja_sama.setVisible( false );
				      sad.set_koniec();
				      ed.set_koniec();
				      ja_sama.dispose();
					 }
					  catch(IOException e){
						  JOptionPane.showMessageDialog(null,e.getMessage(),"Błąd połączenia",JOptionPane.ERROR_MESSAGE); 
					 }
				System.exit(0);
				}
		});
	
		
		JMenu menuPomoc = new JMenu("Pomoc");
		menuPomoc.setMnemonic('P');
		JMenuItem o_programie = menuPomoc.add(new AbstractAction("O programie..."){
			public void actionPerformed(ActionEvent zdarzenie)
				{
				JOptionPane.showMessageDialog(ja_sama,"Klient szachowy.\n Zaliczenie przedmiotu 'Programowanie w Java'\n Konrad Sobczak \n Kamil Nowak \n Wydział Elektryczny Politechniki Warszawskiej 2006","O programie...",JOptionPane.INFORMATION_MESSAGE);
				}
		});
	
		
		JMenuBar pasekMenu = new JMenuBar();
		this.setJMenuBar(pasekMenu);
		pasekMenu.add(menuPlik);
		pasekMenu.add(menuPomoc);
		
		Toolkit zestaw = Toolkit.getDefaultToolkit();
		this.setSize(zestaw.getScreenSize().width/2,zestaw.getScreenSize().height/2);
		
		chat = new JTextPane();
		chat.setPreferredSize(new Dimension(this.getSize().width/2 - 15,zestaw.getScreenSize().height/4));
		chat.setAutoscrolls(true);
		
		
		wpisywanie = new JTextField(30);
		JScrollPane jsp = new JScrollPane(chat);
		jsp.setAutoscrolls(true);
		
		listaStolow = new JList(modelListyStolow);
		listaStolow.setVisibleRowCount(20);
		listaStolow.setPreferredSize(new Dimension(this.getSize().width/2 - 15,zestaw.getScreenSize().height/4));
		listaStolow.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		listaStolow.addMouseListener(new UchwytPrzyciskow());
		
		JScrollPane jsp2 = new JScrollPane(listaStolow);
		
		JLabel stoliczki = new JLabel("Lista stołów: nazwa/właściciel");
		JLabel czacik = new JLabel("Chat:");
		
		Container c = this.getContentPane();
		SpringLayout layout = new SpringLayout();
		c.setLayout(layout);
		c.add(jsp);
		c.add(wpisywanie);
		c.add(jsp2);
		c.add(stoliczki);
		c.add(czacik);
		
		layout.putConstraint(SpringLayout.WEST,stoliczki,10,SpringLayout.WEST,c);
		layout.putConstraint(SpringLayout.NORTH,stoliczki,5,SpringLayout.NORTH,c);
		
		layout.putConstraint(SpringLayout.SOUTH,jsp2,-10,SpringLayout.SOUTH,c);
		layout.putConstraint(SpringLayout.NORTH,jsp2,20,SpringLayout.NORTH,stoliczki);
		layout.putConstraint(SpringLayout.EAST,jsp2,+this.getSize().width/2,SpringLayout.WEST,c);
		layout.putConstraint(SpringLayout.WEST,jsp2,+10,SpringLayout.WEST,c);
		
		layout.putConstraint(SpringLayout.EAST,jsp,-10,SpringLayout.EAST,c);
		layout.putConstraint(SpringLayout.WEST,jsp,+10,SpringLayout.EAST,jsp2);
		layout.putConstraint(SpringLayout.SOUTH,wpisywanie,-5,SpringLayout.SOUTH,c);
		layout.putConstraint(SpringLayout.SOUTH,jsp,-5,SpringLayout.NORTH,wpisywanie);
		layout.putConstraint(SpringLayout.NORTH,jsp,25,SpringLayout.NORTH,c);
		
		layout.putConstraint(SpringLayout.EAST,wpisywanie,-10,SpringLayout.EAST,c);
		layout.putConstraint(SpringLayout.WEST,wpisywanie,0,SpringLayout.WEST,jsp);
		
		layout.putConstraint(SpringLayout.NORTH,czacik,5,SpringLayout.NORTH,c);
		layout.putConstraint(SpringLayout.WEST,czacik,+10,SpringLayout.EAST,jsp2);
		
		wpisywanie.addKeyListener(new UchwytEnteraDlaWpisywania());
		chat.setEditable(false);
	
		this.setVisible(true);
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	
		
		WindowListener windowListener = new WindowAdapter()
		   {   
		   public void windowClosing ( WindowEvent w )
		      {
			  
			  try{
			   if(socket!=null && !socket.isClosed()){
				   PrintWriter out=null;
		    	   out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),Charset.forName("ISO-8859-2")),true);
				   out.println("EXIT");
		    	   socket.close();
			   }
		      ja_sama.setVisible( false );
		      if(sad!=null)
		    	  sad.set_koniec();
		      if(ed!=null)
		    	  ed.set_koniec();
		      ja_sama.dispose();
		      System.exit(0);
			 }
			  catch(IOException e){
				  JOptionPane.showMessageDialog(null,e.getMessage(),"Błąd połączenia",JOptionPane.ERROR_MESSAGE); 
			 }
			  } 
		   };
		this.addWindowListener( windowListener );
		this.pokaz.setEnabled(false);
    	this.chat.setEnabled(false);
    	this.wpisywanie.setEnabled(false);
		this.setMinimumSize(new Dimension(400,300));
    	oknoUrlLogin();
	}
	
	
	private void oknoUrlLogin(){
		  if(wyborUrlLogin==null){
	            wyborUrlLogin= new WyborUrlLogin();
		  }  
	        
	        if(wyborUrlLogin.wyswietlOkno(ja_sama)){
	        	this.imie = wyborUrlLogin.pobierzImie();
	        	
	    		BlockingQueue<String> komunikaty_przychodzace = new LinkedBlockingQueue<String>();
	    		BlockingQueue<String> komunikaty_wychodzace = new LinkedBlockingQueue<String>();
	    		this.menadzerStolow = new TableManager();
	    		CommandExecutor ce = new CommandExecutor(this,this.menadzerStolow);
	    		
	    		
	    		socket=wyborUrlLogin.pobierzSocket();
	    		InputStreamReader inStream;
	    		BufferedReader inread;
	    		PrintWriter out=null;
	    		   
	    		try{
	    			inStream = new InputStreamReader(socket.getInputStream(),Charset.forName("ISO-8859-2"));	
	    			inread =  new BufferedReader(inStream);
	    			out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),Charset.forName("ISO-8859-2")),true);
	    			out.println("HELLO");
	    			if((inread.readLine()).compareTo("OK")==0){
	    				out.println("LOGIN|" + this.imie);
	    				String[] temp = (inread.readLine()).split("\\|");
	    				if(temp[0].compareTo("LOGINOK")==0){
	    					this.id_user=Integer.parseInt(temp[1]);
	    					ja_sama.setTitle("Klient szachowy... - zalogowany id:=" + this.id_user);
	    					sad = new SendingAdapter(socket,komunikaty_przychodzace,komunikaty_wychodzace);
	    					Thread t = new Thread(sad);
	    					t.start();
	    					ed = new EventDispacher(komunikaty_przychodzace,komunikaty_wychodzace,ce);
	    		    		menadzerStolow.setEventDispacher(ed);
	    		    		Thread it = new Thread(ed);
	    		    		it.start();
	    		    		//inread.close();
	    		    		//out.close();
	    		    		this.pokaz.setEnabled(true);
	    			    	this.chat.setEnabled(true);
	    			    	this.wpisywanie.setEnabled(true);
	    				}
	    				else{
	    					System.out.println("tu jestem");
	    					this.pokaz.setEnabled(false);
	    			    	this.chat.setEnabled(false);
	    			    	this.wpisywanie.setEnabled(false);
	    					JOptionPane.showMessageDialog(null,"Użytkownik o tym imieniu już się zalogował!","Błąd logowania",JOptionPane.ERROR_MESSAGE);
	    				}
	    					
	    			}
	    			
	    		}
	    		catch(InterruptedIOException f){
	    			this.pokaz.setEnabled(false);
	    	    	this.chat.setEnabled(false);
	    	    	this.wpisywanie.setEnabled(false);
	    			this.setTitle("Klient szachowy... - niezalogowany");
	    			JOptionPane.showMessageDialog(null,f.getMessage(),"Błąd połączenia",JOptionPane.ERROR_MESSAGE);
	    		}
	    		catch(IOException e){
	    			this.pokaz.setEnabled(false);
	    	    	this.chat.setEnabled(false);
	    	    	this.wpisywanie.setEnabled(false);
	    			this.setTitle("Klient szachowy... - niezalogowany");
	    			JOptionPane.showMessageDialog(null,e.getMessage(),"Błąd połączenia",JOptionPane.ERROR_MESSAGE);
	    		}
	    		
	    	
	    		
	    		
	        }
	        else if(this.socket == null){
	        	this.pokaz.setEnabled(false);
	        	this.chat.setEnabled(false);
	        	this.wpisywanie.setEnabled(false);
	        }
	}
	
	private class NowaSzachownica extends AbstractAction{
		public void actionPerformed(ActionEvent zdarzenie)
		{
			if(wyborNazwaStolu==null){
				wyborNazwaStolu= new NazwaStoluWybor();
			}
	    
	    if(wyborNazwaStolu.wyswietlOkno(ja_sama)){
	    	menadzerStolow.new_table(get_id_user(),imie,wyborNazwaStolu.pobierzNazwe());
	    }
		
		}
	}
	
	/**
	 * Dodaje wypowiedź do chata ogólnego
	 * @param kto - login gracza który przysłął wypowiedź
	 * @param co - treść wypowiedzi
	 */
	public void dodajWypowiedzDoChata(String kto, String co){
		try{	
			String isoco = new String(co.getBytes("iso-8859-2"), "cp1250");
			String isokto = new String(kto.getBytes("iso-8859-2"), "cp1250");
			chat.getDocument().insertString(chat.getDocument().getLength(),"\n" + isokto + ": ",bold);
			chat.getDocument().insertString(chat.getDocument().getLength(),isoco,normal);
			chat.setCaretPosition(chat.getDocument().getLength());
		}
		catch(Exception e){
		
		}
	}
	
	private class UchwytPrzyciskow extends MouseAdapter{
		public void mouseClicked(MouseEvent zdarzenie){
			if(zdarzenie.getClickCount()==2){
				JList source = (JList)zdarzenie.getSource();
				System.out.println(mapaIdStolow.get((String)(source.getSelectedValue())));
				ed.sentJoinMsg(mapaIdStolow.get((String)(source.getSelectedValue())),get_id_user(),get_imie());
			}
		}
	}
	
	private class UchwytEnteraDlaWpisywania implements KeyListener{
		
		public void keyPressed(KeyEvent zdarzenie){
			if(zdarzenie.getKeyCode()==KeyEvent.VK_ENTER){
				
				
				if(wpisywanie.getText().length()>0){
					try{
						
						ed.sendChatMsg(0,id_user,imie,wpisywanie.getText());
					}
					catch(Exception e){
					
					}
					
					wpisywanie.setText("");
				}
			}
		}
		
		public void keyReleased(KeyEvent zdarzenie){
			
		}
		
		public void keyTyped(KeyEvent zdarzenie){
			
		}
	}
	
	private class NazwaStoluWybor extends JPanel{
	    
	    private JDialog okno;
	    private JTextField imieTekst;
	    private boolean ok=false;
	  
	    
	    public NazwaStoluWybor(){
	        setLayout(new BorderLayout());
	        JPanel panel = new JPanel();
	        panel.setLayout(new GridLayout(2,1));
	        panel.add(new JLabel("Nazwa stołu:"));
	        panel.add(imieTekst = new JTextField(20));
	        add(panel,BorderLayout.CENTER);
	        JButton okButton = new JButton("OK");
	        JButton anulujButton = new JButton("Anuluj");
	        
	        
	        okButton.addActionListener(new ActionListener(){
	            public void actionPerformed(ActionEvent zdarzenie){
	            	if(imieTekst.getText().length()>0){
	            		ok=true;
	            		okno.setVisible(false);
	            	}
	            }
	        });
	        
	        anulujButton.addActionListener(new ActionListener(){
	            public void actionPerformed(ActionEvent zdarzenie){
	                okno.setVisible(false);
	            }
	        });
	        JPanel panelPrzyciskow = new JPanel();
	        panelPrzyciskow.add(okButton);
	        panelPrzyciskow.add(anulujButton);
	        add(panelPrzyciskow,BorderLayout.SOUTH);
	    }
	    
	    public String pobierzNazwe(){
	        return imieTekst.getText();
	    }
	    
	    
	    public boolean wyswietlOkno(JFrame rodzic){
	        ok=false;
	        
	        if(okno == null){
	            okno = new JDialog(rodzic,true);
	            okno.getContentPane().add(this);
	            okno.pack();
	        }
	        
	        okno.setTitle("Wpisz nazwę stolu...");
	        okno.setVisible(true);
	        
	        return ok;
	    }
	    
	}
	
	private class WyborUrlLogin extends JPanel{
        
        private JDialog okno;
        private JTextField urlTekst;
        private JTextField imieTekst;
        private boolean ok=false;
       
        private Socket s;
        
        public WyborUrlLogin(){
            setLayout(new BorderLayout());
            JPanel panel = new JPanel();
            panel.setLayout(new GridLayout(2,2));
            panel.add(new JLabel("Url serwera:"));
            panel.add(urlTekst = new JTextField(20));
            panel.add(new JLabel("Imie gracza:"));
            panel.add(imieTekst = new JTextField(20));
            add(panel,BorderLayout.CENTER);
            JButton okButton = new JButton("OK");
            JButton anulujButton = new JButton("Anuluj");
            
          
            okButton.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent zdarzenie){
                    try{
        				if(urlTekst.getText().length()>0 && imieTekst.getText().length()>0){
        					s = new Socket(urlTekst.getText(),18018);
        					s.setSoTimeout(10000);
        					ok=true;
        					okno.setVisible(false);
        					
        				}
        				else{
        					JOptionPane.showMessageDialog(null,"Wpisz Imie i URL!","Źle wpisane dane",JOptionPane.ERROR_MESSAGE);
        				}
                    }
        		catch(IOException e){
        			if(e instanceof UnknownHostException)
        				JOptionPane.showMessageDialog(null,"Nieznany host!","Źle wpisane dane...",JOptionPane.ERROR_MESSAGE);
        			else{
        				JOptionPane.showMessageDialog(null,"Nie można się połączyć","Błąd sieci...",JOptionPane.ERROR_MESSAGE);
        			
        			}
        		}
                    //okno.setVisible(false);
                }
            });
            
            anulujButton.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent zdarzenie){
                    okno.setVisible(false);
                }
            });
            JPanel panelPrzyciskow = new JPanel();
            panelPrzyciskow.add(okButton);
            panelPrzyciskow.add(anulujButton);
           
            add(panelPrzyciskow,BorderLayout.SOUTH);
        }
        
        public String pobierzImie(){
            return imieTekst.getText();
        }
        
        public Socket pobierzSocket(){
        	return this.s;
        }
        
        public boolean wyswietlOkno(JFrame rodzic){
            ok=false;
            
            if(okno == null){
                okno = new JDialog(rodzic,true);
                okno.getContentPane().add(this);
                okno.pack();
            }
            
            okno.setTitle("Wpisz login i adres serwera");
            okno.setVisible(true);
            okno.setLocationRelativeTo(rodzic);
            
            return ok;
        }
        
    }
	
	/**
	 * Zwraca id gracza, jaki otrzymał po połączeniu się z serwerem
	 * @return - zwraca id gracza
	 */
	public long get_id_user(){
		return this.id_user;
	}
	
	/**
	 * Zwraca login użytkownika jaki wybrał sobie przy połączeniu
	 * @return - zwraca imię gracza
	 */
	public  String get_imie(){
		return this.imie;
	}
	
	/**
	 * Czyści listę stołów
	 *
	 */
	public void czysc_liste_stolow(){
		mapaIdStolow = new ConcurrentHashMap<String,Integer>();
		modelListyStolow.clear();
	}
	
	/** 
	 * Dodaje nowy stół do listy
	 * @param nazwa - nazwa stołu
	 * @param id - id stołu otrzymany od serwera
	 */
	public void dodaj_stol(String nazwa, int id){
		mapaIdStolow.put(nazwa,new Integer(id));
		modelListyStolow.addElement(nazwa);
	}
	
	/**
	 * Wyświetla komunikat błędu zakładania stołu
	 *
	 */
	public void blad_zakladania_stolu(){
		JOptionPane.showMessageDialog(this,"Nie można założyć stołu o takiej nazwie","Błąd zakładania stołu",JOptionPane.ERROR_MESSAGE);
	}
	/**
	 * Wyświetla błąd dołączania do stołu
	 *
	 */
	public void blad_joinowania_stolu(){
		JOptionPane.showMessageDialog(this,"Nie można dołączyć stołu o takiej nazwie","Błąd dołączania do stołu",JOptionPane.ERROR_MESSAGE);
	}
	
	/**
	 * Funkcja main
	 * @param args - nie wykorzystywane...
	 */
	public static void main(String[] args){	
		MainWindow g = new MainWindow();
	}
	
}

